﻿using Grpc.Core;
using Microsoft.Extensions.Logging;
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Wicture.DbRESTFul;
using static QPZS.Tecdoc.ArticleSearchRequest.Types;
using static QPZS.Tecdoc.ArticleServiceRpc;

namespace QPZS.Tecdoc
{
    public static class ArticleQueryClient
    {
        private static object locker = new object();
        private static Channel channel;
        private static string channelAddress;

        private const int MaxRetry = 2;

        public static void Init(string address)
        {
            channelAddress = address;
        }
        public static void Init()
        {
            Init(ConfigurationManager.Settings.Variables["TecDoc.ArticleQueryHost"]);
        }

        private static Channel Channel
        {
            get
            {
                if (string.IsNullOrEmpty(channelAddress)) { Init(); }

                if (channel == null || channel.State == ChannelState.Shutdown || channel.State == ChannelState.TransientFailure)
                {
                    try
                    {
                        lock (locker)
                        {
                            if (channel == null || channel.State == ChannelState.Shutdown || channel.State == ChannelState.TransientFailure)
                            {
                                channel?.ShutdownAsync().Wait();
                                channel = null;
                            }

                            if (channel == null)
                            {
                                LoggerManager.Logger.LogInformation($"初始化RPC通道: {channelAddress}。");
                                channel = new Channel(channelAddress, ChannelCredentials.Insecure);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        channel?.ShutdownAsync().Wait();
                        channel = null;
                        LoggerManager.Logger.LogError(ex, $"通道创建失败: {channelAddress}。");
                        throw ex;
                    }
                }

                return channel;
            }
            set
            {
                lock (locker)
                {
                    channel = value;
                }
            }
        }

        public static async Task<ListArticleBySupplierResponse> QueryArticles(int supplierId, int pageIndex, int pageSize)
        {
            try
            {
                return await Call(async client => await client.ListArticleBySupplierAsync(new ListArticleBySupplierRequest
                {
                    SupplierId = supplierId,
                    Pagination = new Pagination { PageIndex = pageIndex, PageSize = pageSize }
                }));
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "Failed to call QuerySuppliers.");
                return null;
            }
        }
        public static async Task<SupplierQueryResponse> QuerySuppliers(string acronym, int pageIndex, int pageSize)
        {
            try
            {
                return await Call(async client => await client.ListSuppliersAsync(new SupplierQueryRequest
                {
                    Acronym = acronym,
                    Pagination = new Pagination { PageIndex = pageIndex, PageSize = pageSize }
                }));
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "Failed to call QuerySuppliers.");
                return null;
            }
        }

        public static async Task<ArticleSearchResponse> Search(string keyword, NumberType numberType = NumberType.Oenumber)
        {
            try
            {
                return await Call(async client => await client.SearchAsync(new ArticleSearchRequest { Keyword = keyword, NumberType = numberType }));
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "Failed to call Search.");
                return null;
            }
        }

        public static async Task<DetailQueryResponse> QueryDetails(string dataSupplierArticleNumber, uint supplierId = 0)
        {
            try
            {
                return await Call(async client => await client.QueryDetailsAsync(new DetailQueryRequest
                {
                    SupplierId = supplierId,
                    DataSupplierArticleNumber = dataSupplierArticleNumber
                }));
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed to call QueryDetails.");
                return null;
            }
        }


        private static async Task<T> Call<T>(Func<ArticleServiceRpcClient, Task<T>> func, int retry = 1, [CallerMemberName]string methodName = "")
        {
            try
            {
                var client = new ArticleServiceRpcClient(Channel);
                return await func.Invoke(client);
            }
            catch (RpcException ex)
            {
                LoggerManager.Logger.LogError(ex, $"调用 ArticleServiceRpcClient.{methodName} 出错。");
                Channel = null;
                return retry >= MaxRetry ? default(T) : await Call(func, retry + 1, methodName);
            }
        }

    }
}
